package com.atguigu.rsm.aspect;

import com.atguigu.rsm.annotations.MyRedisCache;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@Component
@Aspect


public class MyRedisCacheAspect {

    @Resource
    private RedisTemplate redisTemplate;

    //配置切入点
    @Pointcut("@annotation(com.atguigu.rsm.annotations.MyRedisCache)")
    public void cachPointcut(){}

    @Around("cachPointcut()")
    public Object doCache(ProceedingJoinPoint joinPoint){
        Object result = null;

        /**
         *     @MyRedisCache(keyPrefix = "user",matchValue = "#id")
         *     public User getUserById(Integer id)
         *     {
         *         return userMapper.selectByPrimaryKey(id);
         *     }
         */

        try {
            //1.获得重载后的方法名


            MethodSignature signature =(MethodSignature)joinPoint.getSignature();
            Method method = signature.getMethod();

            //2.确定方法名后获得该方法上面配置的注解标签MyRedisCache

            MyRedisCache myRedisCacheAnnotation =  method.getAnnotation(MyRedisCache.class);

            //3.拿到了MyRedisCache的注解标签，获得该注解上配置的参数进行封装和调用
            String keyPrefix = myRedisCacheAnnotation.keyPrefix();
            String matchValueSpringEl = myRedisCacheAnnotation.matchValue();

            //4.SpringEL 解析器
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(matchValueSpringEl);
            EvaluationContext context = new StandardEvaluationContext();

            //5.使用反射获得方法内的形参信息
            Object[] args = joinPoint.getArgs();
            Parameter[] parameterNames = method.getParameters();
            for (int i = 0; i < parameterNames.length; i++){
                System.out.println(("获得方法里参数名和值：" + parameterNames[i].getName() + "\t" + args[i].toString()));
                context.setVariable(parameterNames[i].getName(), args[i].toString());
            }

            //6.通过上述，拼接redis的最终key形式
            String key = keyPrefix + ":" + expression.getValue(context).toString();
            System.out.println(("---------拼接redis的最终key形式：" + key));

            //7.先去redis查询是否存在
            result=redisTemplate.opsForValue().get(key);
            if (result != null){
                System.out.println("------redis里面有，直接返回结果不打扰mysql" + result);
                return result;
            }

            //8.redis里面没有，去找mysql查询或进行后续业务逻辑
            //-------aop部分,才去找getUserById方法干活
            //主业务逻辑查询mysql,放行放行放行userMapper.selectById(id)


            result = joinPoint.proceed();

            //9 mysql步骤结束，还需要把结果存入redis一次，缓存补偿

            if (result != null){
                System.out.println("----------redis里面无，还需要把结果存入redis一次，缓存补偿：" + result);
                redisTemplate.opsForValue().set(key, result);

            }
        } catch (Throwable e) {
            e.printStackTrace();
        }

        return result;


    }

}
